const axios = require('axios');
const fs = require('fs');
const path = require('path');
require('dotenv').config();
const schedule = require('node-schedule');
const moment = require('moment');
const logger = require('./logger');
const binanceApi = require('./binance');
const mysql = require('./mysql');

// 全局配置变量
let config = null;

// 配置文件路径
const CONFIG_PATH = path.join(__dirname, '../../src/config/monitor.json');

// 上次提醒记录
const lastAlerts = new Map();
const lastAlertCounts = new Map();  // 添加这行来记录上次提醒的K线数量
// 记录服务启动时间，用于避免推送历史数据
const serviceStartTime = Date.now();

// 生成提醒记录的key
function generateAlertKey(symbol, interval, count, type, trendKlines) {
    // 使用K线集合的第一根和最后一根K线的时间戳作为唯一标识
    // 这样确保不同检查点检查到相同的连续K线不会重复推送
    if (!trendKlines || trendKlines.length === 0) {
        return `${symbol}-${interval}-${count}-${type}`;
    }
    
    const firstKline = trendKlines[0];
    const lastKline = trendKlines[trendKlines.length - 1];
    
    const firstTime = firstKline.open_time.getTime();
    const lastTime = lastKline.open_time.getTime();
    
    return `${symbol}-${interval}-${count}-${type}-${firstTime}-${lastTime}`;
}

// 加载配置文件
function loadConfig() {
    try {
        // 尝试多个可能的配置文件路径
        const possiblePaths = [
            path.join(__dirname, '../../config/config.json'),
            path.join(__dirname, '../../../config/config.json'),
            path.join(process.cwd(), 'config/config.json'),
            path.join(process.cwd(), 'src/config/monitor.json')
        ];
        
        // 详细记录尝试的路径
        logWithCstTime('尝试加载配置文件...');
        for (const configPath of possiblePaths) {
            logWithCstTime(`尝试路径: ${configPath}`);
            if (fs.existsSync(configPath)) {
                logWithCstTime(`找到配置文件: ${configPath}`);
                const configData = fs.readFileSync(configPath, 'utf8');
                const parsedConfig = JSON.parse(configData);
                logWithCstTime(`成功加载配置: ${JSON.stringify(parsedConfig)}`);
                return parsedConfig;
            }
        }
        
        // 如果所有路径都失败，尝试使用硬编码的配置
        logWithCstTime('所有配置文件路径尝试失败，使用硬编码的默认配置');
        return {
            symbols: ["BTCUSDT", "ETHUSDT"],
            intervals: ["10m", "30m", "1h"],
            consecutiveCount: 4,
            checkPoints: {
                "10m": [8, 18, 28, 38, 48, 58],
                "30m": [28, 58],
                "1h": [58]
            }
        };
    } catch (error) {
        logWithCstTime(`加载配置出错: ${error.message}`);
        logWithCstTime('使用硬编码的默认配置');
        // 返回一个硬编码的基本配置
        return {
            symbols: ["BTCUSDT", "ETHUSDT"],
            intervals: ["10m", "30m", "1h"],
            consecutiveCount: 4,
            checkPoints: {
                "10m": [8, 18, 28, 38, 48, 58],
                "30m": [28, 58],
                "1h": [58]
            }
        };
    }
}

// 企业微信机器人配置
const WECOM_WEBHOOKS = [
    // 标准机器人地址
    'https://qyapi.weixin.qq.com/cgi-bin/webhook/send?key=8a62ade4-ac5d-4d79-a8ae-e60472f9ff03'
];

// 提前预警机器人配置（特殊机器人，比普通机器人提前1根K线报警）
const EARLY_WARNING_WEBHOOKS = [
    'https://qyapi.weixin.qq.com/cgi-bin/webhook/send?key=2c840647-1395-4b5e-8fd1-0ca7ae883092',
    'https://qyapi.weixin.qq.com/cgi-bin/webhook/send?key=2ac180f1-8b64-4b76-a229-29d1062846d2'
];

// 自定义日志函数，打印CST时间
function logWithCstTime(message) {
    const now = new Date();
    // 将UTC时间转换为CST时间（+8小时）
    const cstTime = new Date(now.getTime() + 8 * 60 * 60 * 1000);
    const timeString = cstTime.toISOString().replace('T', ' ').replace('Z', '');
    console.log(`[${timeString} CST] ${message}`);
}

// 获取K线数据
async function getKlines(symbol, interval, limit) {
    try {
        // 确保limit是一个有效的数字
        const validLimit = typeof limit === 'number' && !isNaN(limit) ? limit : 60;
        
        logWithCstTime(`开始获取币安K线数据: ${symbol} ${interval} limit=${validLimit}`);
        
        const response = await axios.get('https://api.binance.com/api/v3/klines', {
            params: {
                symbol: symbol,
                interval: interval,
                limit: validLimit
            },
            headers: {
                'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/96.0.4664.110 Safari/537.36'
            }
        });
        
        if (!response.data || !Array.isArray(response.data) || response.data.length === 0) {
            logWithCstTime(`获取K线数据失败，响应数据为空或无效: ${JSON.stringify(response.data)}`);
            return null;
        }
        
        logWithCstTime(`成功获取${response.data.length}条K线数据`);
        
        const now = Date.now();
        
        // 转换数据格式并进行验证
        const klines = response.data.map(k => {
            // 验证数据完整性
            if (k.length < 11) {
                logWithCstTime(`K线数据不完整: ${JSON.stringify(k)}`);
                return null;
            }
            
            const openTime = new Date(parseInt(k[0]));
            const closeTime = new Date(parseInt(k[6]));
            const openPrice = k[1];
            const highPrice = k[2];
            const lowPrice = k[3];
            const closePrice = k[4];
            
            // 检查价格数据的合理性
            if (parseFloat(lowPrice) > parseFloat(highPrice)) {
                logWithCstTime(`K线价格数据不合理，最低价大于最高价: ${lowPrice} > ${highPrice}, 时间: ${openTime.toISOString()}`);
                return null;
            }
            
            // 检查时间数据的合理性
            if (closeTime < openTime) {
                logWithCstTime(`K线时间数据不合理，收盘时间早于开盘时间: ${closeTime.toISOString()} < ${openTime.toISOString()}`);
                return null;
            }
            
            // 检查是否是未来的K线
            if (openTime.getTime() > now) {
                logWithCstTime(`K线开盘时间在未来: ${openTime.toISOString()}, 当前时间: ${new Date(now).toISOString()}`);
                return null;
            }
            
            return {
                open_time: openTime,
                open_price: openPrice,
                high_price: highPrice,
                low_price: lowPrice,
                close_price: closePrice,
                volume: k[5],
                close_time: closeTime
            };
        }).filter(k => k !== null);
        
        // 检查是否有重复的K线
        const timeMap = new Map();
        const uniqueKlines = [];
        
        for (const kline of klines) {
            const timeKey = kline.open_time.getTime();
            if (!timeMap.has(timeKey)) {
                timeMap.set(timeKey, kline);
                uniqueKlines.push(kline);
            } else {
                logWithCstTime(`发现重复的K线数据，时间: ${kline.open_time.toISOString()}`);
            }
        }
        
        // 按时间排序，最新的在前面
        uniqueKlines.sort((a, b) => b.open_time.getTime() - a.open_time.getTime());
        
        logWithCstTime(`过滤和排序后剩余${uniqueKlines.length}条有效K线数据`);
        
        // 检查最新K线的时间是否合理
        if (uniqueKlines.length > 0) {
            const latestKline = uniqueKlines[0];
            const intervalMs = getIntervalMilliseconds(interval);
            const expectedLatestTime = new Date(Math.floor(now / intervalMs) * intervalMs - intervalMs);
            
            const timeDiff = Math.abs(latestKline.open_time.getTime() - expectedLatestTime.getTime());
            if (timeDiff > intervalMs) {
                logWithCstTime(`警告：最新K线时间(${latestKline.open_time.toISOString()})与期望时间(${expectedLatestTime.toISOString()})相差超过一个周期`);
            } else {
                logWithCstTime(`最新K线时间符合预期`);
            }
        }
        
        return uniqueKlines;
    } catch (error) {
        logWithCstTime('获取K线数据失败:', error);
        return null;
    }
}

// 获取时间间隔的毫秒数
function getIntervalMilliseconds(interval) {
    const match = interval.match(/(\d+)([mhdw])/);
    if (!match) return 60 * 60 * 1000; // 默认1小时
    
    const value = parseInt(match[1]);
    const unit = match[2];
    
    switch (unit) {
        case 'm': return value * 60 * 1000; // 分钟
        case 'h': return value * 60 * 60 * 1000; // 小时
        case 'd': return value * 24 * 60 * 60 * 1000; // 天
        case 'w': return value * 7 * 24 * 60 * 60 * 1000; // 周
        default: return 60 * 60 * 1000; // 默认1小时
    }
}

// 获取当前市场价格
async function getCurrentPrice(symbol) {
    try {
        const response = await axios.get('https://api.binance.com/api/v3/ticker/price', {
            params: { symbol }
        });
        return parseFloat(response.data.price);
    } catch (error) {
        logWithCstTime('获取当前价格失败:', error);
        return null;
    }
}

// 合成10分钟K线
async function generate10MinKlines(symbol) {
    try {
        // 获取更多K线以确保能覆盖到最新的完整K线
        const limit = 50; // 增加获取的K线数量，确保数据充足
        logWithCstTime(`获取 ${symbol} 5分钟K线，数量: ${limit}`);
        const klines = await getKlines(symbol, '5m', limit);
        if (!klines || klines.length < 12) {
            logWithCstTime(`5分钟K线数据不足，需要至少12根，实际获取 ${klines ? klines.length : 0} 根`);
            return null;
        }

        // 打印获取到的原始K线时间顺序和价格，方便调试
        logWithCstTime(`获取到的5分钟K线原始数据（最新10根）:`);
        const sortedRawKlines = [...klines].sort((a, b) => b.open_time.getTime() - a.open_time.getTime());
        for (let i = 0; i < Math.min(10, sortedRawKlines.length); i++) {
            const k = sortedRawKlines[i];
            logWithCstTime(`  原始K线 #${i+1}: ${k.open_time.toISOString()} 开:${k.open_price} 收:${k.close_price} 涨跌:${parseFloat(k.close_price) > parseFloat(k.open_price) ? '上涨' : parseFloat(k.close_price) < parseFloat(k.open_price) ? '下跌' : '平'}`);
        }

        // 按时间对K线进行排序（从早到晚）
        klines.sort((a, b) => a.open_time.getTime() - b.open_time.getTime());

        const tenMinKlines = [];
        // 每两根5分钟K线合成一根10分钟K线
        for (let i = 0; i < klines.length - 1; i += 2) {
            const k1 = klines[i];
            const k2 = klines[i + 1];
            
            // 检查时间是否对齐
            const k1Time = k1.open_time.getTime();
            const k2Time = k2.open_time.getTime();
            if (k2Time - k1Time !== 300000) { // 5分钟 = 300000毫秒
                logWithCstTime(`时间不连续: k1=${k1.open_time.toISOString()}, k2=${k2.open_time.toISOString()}`);
                continue;
            }

            // 检查是否是完整的K线（最后一根K线可能未完成）
            const now = Date.now();
            if (k2.close_time.getTime() > now) {
                logWithCstTime(`跳过未完成的K线: ${k2.open_time.toISOString()} ~ ${k2.close_time.toISOString()}`);
                continue;
            }
            
            // 详细记录合成K线的开盘价和收盘价
            const openPrice = k1.open_price;
            const closePrice = k2.close_price;
            const highPrice = Math.max(parseFloat(k1.high_price), parseFloat(k2.high_price)).toString();
            const lowPrice = Math.min(parseFloat(k1.low_price), parseFloat(k2.low_price)).toString();
            
            const newKline = {
                open_time: k1.open_time,
                open_price: openPrice,
                high_price: highPrice,
                low_price: lowPrice,
                close_price: closePrice,
                volume: (parseFloat(k1.volume) + parseFloat(k2.volume)).toString(),
                close_time: k2.close_time
            };
            
            // 判断涨跌
            const change = parseFloat(closePrice) - parseFloat(openPrice);
            const trend = change > 0 ? '上涨' : change < 0 ? '下跌' : '平';
            
            logWithCstTime(`合成10分钟K线: ${k1.open_time.toISOString()} ~ ${k2.close_time.toISOString()} 开:${openPrice} 收:${closePrice} 涨跌:${trend}`);
            
            tenMinKlines.push(newKline);
        }
        
        // 按照时间排序，最新的在前面
        tenMinKlines.sort((a, b) => b.open_time.getTime() - a.open_time.getTime());
        
        // 打印生成的10分钟K线
        logWithCstTime(`生成的10分钟K线（最新10根）:`);
        for (let i = 0; i < Math.min(10, tenMinKlines.length); i++) {
            const k = tenMinKlines[i];
            const openPrice = parseFloat(k.open_price);
            const closePrice = parseFloat(k.close_price);
            const change = closePrice - openPrice;
            const trend = change > 0 ? '上涨' : change < 0 ? '下跌' : '平';
            
            logWithCstTime(`  合成K线 #${i+1}: ${k.open_time.toISOString()} ~ ${k.close_time.toISOString()}, 开:${k.open_price}, 收:${k.close_price}, 涨跌:${trend}, 变化:${change.toFixed(2)}`);
        }
        
        // 检查最新K线的时间是否在预期范围内
        if (tenMinKlines.length > 0) {
            const mostRecentKline = tenMinKlines[0];
            const now = new Date();
            const tenMinMs = 10 * 60 * 1000;
            const expectedLatestTime = new Date(Math.floor(now.getTime() / tenMinMs) * tenMinMs - tenMinMs);
            
            const timeDiff = Math.abs(mostRecentKline.open_time.getTime() - expectedLatestTime.getTime());
            if (timeDiff > tenMinMs) {
                logWithCstTime(`警告：合成的10分钟K线最新时间(${mostRecentKline.open_time.toISOString()})与期望时间(${expectedLatestTime.toISOString()})相差超过10分钟`);
            } else {
                logWithCstTime(`合成的10分钟K线时间符合预期`);
            }
            
            // 检查最新几根K线的涨跌情况
            logWithCstTime(`最新3根10分钟K线的涨跌情况:`);
            for (let i = 0; i < Math.min(3, tenMinKlines.length); i++) {
                const k = tenMinKlines[i];
                const openPrice = parseFloat(k.open_price);
                const closePrice = parseFloat(k.close_price);
                const change = closePrice - openPrice;
                const trend = change > 0 ? '上涨' : change < 0 ? '下跌' : '平';
                
                logWithCstTime(`  K线 #${i+1}: ${k.open_time.toISOString()}, 开:${k.open_price}, 收:${k.close_price}, 涨跌:${trend}, 变化:${change.toFixed(2)}`);
            }
        }
        
        return tenMinKlines;
    } catch (error) {
        logWithCstTime('合成10分钟K线失败:', error);
        return null;
    }
}

// 检查是否是需要执行检查的时间点
function shouldCheck(minute, interval) {
    try {
        // 记录当前系统时间详情，用于调试
        const now = new Date();
        logWithCstTime(`shouldCheck调用 - 系统时间: ${now.toISOString()}, 当前分钟: ${minute}, 周期: ${interval}`);
        
        // 如果配置未正确加载，则重新加载
        if (!config || !config.checkPoints) {
            logWithCstTime('配置未正确加载，尝试加载配置');
            config = loadConfig();
            if (!config || !config.checkPoints) {
                logWithCstTime('配置未正确加载，跳过检查');
                return false;
            }
            logWithCstTime(`配置加载成功，检查点: ${JSON.stringify(config.checkPoints)}`);
        }
        
        // 获取当前时间间隔的检查点配置
        const checkPoints = config.checkPoints[interval];
        
        // 如果没有为当前时间间隔配置检查点，则返回false
        if (!checkPoints || !Array.isArray(checkPoints)) {
            logWithCstTime(`未配置${interval}的检查点，跳过检查`);
            return false;
        }
        
        logWithCstTime(`${interval}的配置检查点: ${checkPoints.join(', ')}`);
        
        // 检查当前分钟是否是配置的检查点
        const isCheckPoint = checkPoints.includes(minute);
        
        if (isCheckPoint) {
            logWithCstTime(`当前分钟 ${minute} 是 ${interval} 的检查点，开始检查`);
        } else {
            // 记录没有执行检查的原因
            const nearestPoint = checkPoints.reduce((nearest, point) => 
                Math.abs(point - minute) < Math.abs(nearest - minute) ? point : nearest, 
                checkPoints[0]
            );
            const minutesDiff = Math.abs(nearestPoint - minute);
            logWithCstTime(`当前分钟 ${minute} 不是 ${interval} 的检查点 ${checkPoints.join(',')}，最近的检查点是 ${nearestPoint} 分钟，相差 ${minutesDiff} 分钟，跳过检查`);
            
            // 如果相差只有1分钟，可能是系统时间略微不准或处理延迟，记录警告
            if (minutesDiff <= 1) {
                logWithCstTime(`警告：当前分钟 ${minute} 与最近检查点 ${nearestPoint} 只相差 ${minutesDiff} 分钟，可能是因为系统时间不准确或处理延迟`);
            }
        }
        
        return isCheckPoint;
    } catch (error) {
        logWithCstTime(`检查时间点出错: ${error.message}`);
        logWithCstTime(`错误堆栈: ${error.stack}`);
        return false;
    }
}

// 检查连续K线趋势并预测
async function checkConsecutiveKlinesAndPredict(symbol, interval, forceCheck = false) {
    try {
        // 检查是否应该在当前时间检查该间隔
        const now = new Date();
        
        // 添加详细的系统时间日志
        logWithCstTime(`系统时间详情 - 年: ${now.getFullYear()}, 月: ${now.getMonth() + 1}, 日: ${now.getDate()}, 时: ${now.getHours()}, 分: ${now.getMinutes()}, 秒: ${now.getSeconds()}`);
        logWithCstTime(`系统时间ISO格式: ${now.toISOString()}, 时间戳: ${now.getTime()}`);
        logWithCstTime(`系统时间本地字符串: ${now.toString()}`);
        logWithCstTime(`强制检查模式: ${forceCheck ? '是' : '否'}`);
        
        // 如果配置文件没有加载成功，尝试加载
        if (!config) {
            logWithCstTime('配置文件未加载，尝试加载配置...');
            config = loadConfig();
            if (!config) {
                logWithCstTime('配置文件加载失败，无法执行监控任务');
                return null;
            }
            logWithCstTime('配置文件加载成功');
        }
        
        // 获取K线数据
        let klines;
        if (interval === '10m') {
            logWithCstTime(`开始获取${symbol} ${interval}周期K线数据（生成10分钟K线）...`);
            klines = await generate10MinKlines(symbol);
        } else {
            // 增加获取的K线数量，确保有足够的最新数据
            logWithCstTime(`开始获取${symbol} ${interval}周期K线数据（直接获取）...`);
            klines = await getKlines(symbol, interval, 50); // 从20增加到50，获取更多历史数据
        }
        
        if (!klines || klines.length < 6) {
            logWithCstTime(`K线数据不足，需要至少6根，实际获取 ${klines ? klines.length : 0} 根`);
            return null;
        }
        
        logWithCstTime(`成功获取${klines.length}根K线数据`);
        
        // 获取当前价格
        logWithCstTime(`开始获取${symbol}当前价格...`);
        const currentPrice = await getCurrentPrice(symbol);
        if (!currentPrice) {
            logWithCstTime(`获取当前价格失败`);
            return null;
        }
        logWithCstTime(`当前${symbol}价格: ${currentPrice}`);
        
        // 打印当前系统时间
        logWithCstTime(`当前系统时间: ${now.toISOString()} (UTC)`);
        
        // 将K线按时间排序，最新的排在前面
        klines.sort((a, b) => b.open_time.getTime() - a.open_time.getTime());
        
        // 确定当前周期的时间长度（毫秒）
        let intervalMs;
        if (interval === '10m') {
            intervalMs = 10 * 60 * 1000;
        } else if (interval === '30m') {
            intervalMs = 30 * 60 * 1000;
        } else if (interval === '1h') {
            intervalMs = 60 * 60 * 1000;
        } else if (interval === '1d') {
            intervalMs = 24 * 60 * 60 * 1000;
        } else {
            intervalMs = 60 * 60 * 1000; // 默认1小时
        }
        logWithCstTime(`${interval}周期对应的毫秒数: ${intervalMs}ms`);
        
        // 计算当前K线的开始时间（对齐到K线周期）
        const currentKlineStartTime = new Date(Math.floor(now.getTime() / intervalMs) * intervalMs);
        logWithCstTime(`计算当前K线开始时间: ${currentKlineStartTime.toISOString()}, 时间戳: ${currentKlineStartTime.getTime()}`);
        
        // 找到当前正在形成的K线
        let currentKline = null;
        for (const kline of klines) {
            // 如果K线开始时间与当前K线开始时间相差不到一个周期，则认为是当前K线
            const timeDiff = Math.abs(kline.open_time.getTime() - currentKlineStartTime.getTime());
            logWithCstTime(`检查K线 ${kline.open_time.toISOString()}, 时间差: ${timeDiff}ms, 阈值: ${intervalMs}ms`);
            if (timeDiff < intervalMs) {
                currentKline = kline;
                logWithCstTime(`找到当前正在形成的K线: ${kline.open_time.toISOString()}`);
                break;
            }
        }
        
        if (!currentKline) {
            logWithCstTime(`未找到当前正在形成的K线，这可能是因为系统时间与K线数据时间不匹配`);
        }
        
        // 获取已完成的K线
        const completedKlines = [];
        for (const kline of klines) {
            // 如果K线不是当前K线，且已经完成（收盘时间早于当前时间）
            if (kline !== currentKline && kline.close_time <= now) {
                completedKlines.push(kline);
                logWithCstTime(`添加已完成K线: ${kline.open_time.toISOString()}, 收盘时间: ${kline.close_time.toISOString()}`);
            }
        }
        
        // 记录找到的K线信息
        logWithCstTime(`当前K线时间: ${currentKline ? currentKline.open_time.toISOString() : '未找到'}`);
        logWithCstTime(`已完成K线数量: ${completedKlines.length}`);
        
        // 如果是强制检查模式，记录所有K线信息
        if (forceCheck) {
            logWithCstTime(`强制检查模式 - 所有K线时间详情：`);
            klines.slice(0, 10).forEach((k, i) => {
                logWithCstTime(`K线 #${i+1}: 开盘时间=${k.open_time.toISOString()}, 收盘时间=${k.close_time.toISOString()}, 开盘价=${k.open_price}, 收盘价=${k.close_price}`);
            });
        }
        
        // 如果没有足够的已完成K线，则跳过
        if (completedKlines.length < 3) { // 改为3根，因为提前预警需要至少3根
            logWithCstTime(`已完成K线数量不足，需要至少3根，当前有 ${completedKlines.length} 根`);
            return null;
        }
        
        // 根据不同的时间间隔执行不同的检查逻辑
        let result = null;
        
        if (interval === '10m') {
            logWithCstTime(`执行10m周期特定检查逻辑...`);
            result = check10mKlines(symbol, interval, completedKlines, currentKline, currentPrice, now, forceCheck);
        } else {
            logWithCstTime(`执行${interval}周期通用检查逻辑...`);
            result = checkLongerIntervalKlines(symbol, interval, completedKlines, currentKline, currentPrice, now, forceCheck);
        }
        
        // 记录检查结果
        if (result) {
            logWithCstTime(`检查结果: 找到了${result.count}根连续${result.type === 'up' ? '上涨' : '下跌'}K线`);
        } else {
            logWithCstTime(`检查结果: 未找到符合条件的连续K线`);
            
            // 如果常规检查没有结果，尝试提前预警
            logWithCstTime(`尝试执行提前预警检查...`);
            const earlyWarningResult = checkEarlyWarningTrend(symbol, interval, completedKlines, currentKline, currentPrice, now, forceCheck);
            
            if (earlyWarningResult) {
                logWithCstTime(`提前预警检查触发了提醒：${earlyWarningResult.type} ${earlyWarningResult.count}根`);
                return earlyWarningResult;
            } else {
                logWithCstTime(`提前预警检查没有触发提醒`);
            }
        }
        
        return result;
    } catch (error) {
        logWithCstTime(`检查连续K线出错: ${error.message}`);
        logWithCstTime(`错误堆栈: ${error.stack}`);
        return null;
    }
}

// 检查10分钟K线趋势
function check10mKlines(symbol, interval, completedKlines, currentKline, currentPrice, now, forceCheck = false) {
    // 现在寻找全部的连续K线，不仅仅是固定数量
    const minRequiredCount = 3; // 正式环境为至少3根连续K线
    let consecutiveUp = 0;
    let consecutiveDown = 0;
    let trendKlines = [];
    
    // 按时间排序K线，最近的K线排在前面
    completedKlines.sort((a, b) => b.open_time.getTime() - a.open_time.getTime());
    
    // 记录排序后的K线时间顺序
    logWithCstTime(`检查10分钟K线趋势，排序后K线时间（最新5根）:`);
    for (let i = 0; i < Math.min(5, completedKlines.length); i++) {
        const k = completedKlines[i];
        const openPrice = parseFloat(k.open_price);
        const closePrice = parseFloat(k.close_price);
        const change = closePrice - openPrice;
        const trend = change > 0 ? '上涨' : change < 0 ? '下跌' : '平';
        logWithCstTime(`  K线 #${i+1}: ${k.open_time.toISOString()}, 开:${k.open_price}, 收:${k.close_price}, 涨跌:${trend}, 变化:${change.toFixed(2)}`);
    }
    
    // 计算我们期望的最近K线的开始时间
    const intervalMs = 10 * 60 * 1000; // 10分钟
    const expectedLatestTime = new Date(Math.floor(now.getTime() / intervalMs) * intervalMs - intervalMs);
    logWithCstTime(`当前时间: ${now.toISOString()}, 期望最近K线时间: ${expectedLatestTime.toISOString()}`);
    
    // 验证数据的时间连续性，只使用最近6小时内的K线
    const cutoffTime = new Date(now.getTime() - 6 * 60 * 60 * 1000);
    let validKlines = completedKlines.filter(kline => kline.open_time >= cutoffTime);
    
    // 再次确认最近的K线确实是我们期望的时间范围
    if (validKlines.length > 0) {
        const mostRecentKline = validKlines[0];
        const timeDiff = Math.abs(mostRecentKline.open_time.getTime() - expectedLatestTime.getTime());
        
        if (timeDiff > intervalMs) {
            logWithCstTime(`警告：最近的K线时间(${mostRecentKline.open_time.toISOString()})与期望时间(${expectedLatestTime.toISOString()})相差超过一个周期`);
        } else {
            logWithCstTime(`最近K线时间符合预期`);
        }
    }
    
    // 从最新的K线开始，检查连续的涨跌情况
    for (let i = 0; i < validKlines.length; i++) {
        const kline = validKlines[i];
        const openPrice = parseFloat(kline.open_price);
        const closePrice = parseFloat(kline.close_price);
        const change = closePrice - openPrice;
        const trend = change > 0 ? '上涨' : change < 0 ? '下跌' : '平';

        // 详细记录每个K线的信息，包括时间和涨跌情况
        logWithCstTime(`检查K线 #${i+1}: ${kline.open_time.toISOString()}, 开:${openPrice}, 收:${closePrice}, 涨跌:${trend}, 变化:${change.toFixed(2)}`);

        // 判断当前K线是涨还是跌
        const isUp = change > 0;
        const isDown = change < 0;
        
        // 如果是第一根K线，初始化趋势
        if (trendKlines.length === 0) {
            if (isUp) {
                consecutiveUp = 1;
                consecutiveDown = 0;
                trendKlines.push(kline);
                logWithCstTime(`  开始上涨趋势，K线开始时间: ${kline.open_time.toISOString()}`);
            } else if (isDown) {
                consecutiveUp = 0;
                consecutiveDown = 1;
                trendKlines.push(kline);
                logWithCstTime(`  开始下跌趋势，K线开始时间: ${kline.open_time.toISOString()}`);
            } else {
                // 平盘，跳过
                logWithCstTime(`  跳过平盘K线: ${kline.open_time.toISOString()}`);
                continue;
            }
        } else {
            // 判断是否与当前趋势一致
            if (isUp && consecutiveUp > 0) {
                consecutiveUp++;
                trendKlines.push(kline);
                logWithCstTime(`  继续上涨趋势，当前连续上涨: ${consecutiveUp}根`);
            } else if (isDown && consecutiveDown > 0) {
                consecutiveDown++;
                trendKlines.push(kline);
                logWithCstTime(`  继续下跌趋势，当前连续下跌: ${consecutiveDown}根`);
            } else {
                // 趋势不一致，结束检查
                logWithCstTime(`  趋势不一致，中断检查`);
                break;
            }
        }
    }
    
    // 不改变原始trendKlines的排序，formatMessage函数将负责排序
    logWithCstTime(`找到的连续K线（按发现顺序）: ${trendKlines.length}根，类型: ${consecutiveUp > 0 ? '上涨' : '下跌'}`);
    trendKlines.forEach((k, i) => {
        const openPrice = parseFloat(k.open_price);
        const closePrice = parseFloat(k.close_price);
        const change = closePrice - openPrice;
        const trend = change > 0 ? '上涨' : change < 0 ? '下跌' : '平';
        logWithCstTime(`  K线 #${i+1}: ${k.open_time.toISOString()}, 开:${k.open_price}, 收:${k.close_price}, 涨跌:${trend}, 变化:${change.toFixed(2)}`);
    });
    
    // 检查是否达到了最小连续数量，并且是最近的K线
    if ((consecutiveUp >= minRequiredCount || consecutiveDown >= minRequiredCount) && 
        trendKlines.length >= minRequiredCount && 
        currentKline) {
        
        // 按时间排序，用于检查
        const sortedKlines = [...trendKlines].sort((a, b) => a.open_time.getTime() - b.open_time.getTime());
        const firstKlineTime = sortedKlines[0].open_time.getTime();
        const lastKlineTime = sortedKlines[sortedKlines.length - 1].open_time.getTime();
        
        // 最后的安全检查：确保找到的连续K线确实是最近的K线
        const expectedTime = expectedLatestTime.getTime();
        
        // 如果最近的K线与期望时间相差超过30分钟，这可能是不正确的数据
        if (Math.abs(lastKlineTime - expectedTime) > 3 * intervalMs) {
            logWithCstTime(`警告：找到的连续K线不是最近的K线。最新K线时间: ${new Date(lastKlineTime).toISOString()}, 期望时间: ${expectedLatestTime.toISOString()}`);
            logWithCstTime(`相差时间过大，不触发提醒`);
            return null;
        }
        
        // 检查当前K线的趋势
        const currentOpenPrice = parseFloat(currentKline.open_price);
        const currentPriceChange = currentPrice - currentOpenPrice;
        const currentDirection = currentPriceChange > 0 ? 'up' : currentPriceChange < 0 ? 'down' : null;
        
        // 确定是向上还是向下的趋势
        const trend = consecutiveUp > 0 ? 'up' : 'down';
        
        // 格式化通知消息
        const message = formatMessage(symbol, interval, currentDirection, trendKlines, currentKline, currentPrice, now, forceCheck);
        
        return {
            type: trend,
            count: trendKlines.length,
            predictedCount: trendKlines.length + 1,
            currentPrice: currentPrice,
            currentChangePercent: ((currentPrice - currentOpenPrice) / currentOpenPrice * 100).toFixed(2),
            message: message,
            isCompleted: false,
            trendKlines: trendKlines
        };
    }
    
    return null;
}

// 检查30分钟和1小时K线趋势
function checkLongerIntervalKlines(symbol, interval, completedKlines, currentKline, currentPrice, now, forceCheck = false) {
    // 获取最少需要的连续K线数量，但不限制最大数量
    const minRequiredCount = config && config.consecutiveCount ? config.consecutiveCount : 4;
    logWithCstTime(`${interval}需要的最小连续K线数量: ${minRequiredCount}`);
    
    // 按时间排序K线，最近的K线排在前面
    const sortedKlines = [...completedKlines].sort((a, b) => b.open_time.getTime() - a.open_time.getTime());
    
    // 验证数据的时间连续性，只使用最近时间段内的K线
    let intervalMs;
    if (interval === '10m') {
        intervalMs = 10 * 60 * 1000;
    } else if (interval === '30m') {
        intervalMs = 30 * 60 * 1000;
    } else if (interval === '1h') {
        intervalMs = 60 * 60 * 1000;
    } else if (interval === '1d') {
        intervalMs = 24 * 60 * 60 * 1000;
    } else {
        intervalMs = 60 * 60 * 1000; // 默认1小时
    }
    
    // 计算"最近"的定义：12个周期之内的K线
    const cutoffTime = new Date(now.getTime() - 12 * intervalMs);
    logWithCstTime(`设置过滤时间点: ${cutoffTime.toISOString()}，该时间之前的K线将被过滤`);
    
    // 筛选最近12个周期内的K线
    const validKlines = sortedKlines.filter(kline => kline.open_time >= cutoffTime);
    logWithCstTime(`过滤后的K线数量: ${validKlines.length}/${sortedKlines.length}`);
    
    // 记录排序后的K线时间顺序，帮助调试
    logWithCstTime(`${interval}周期 - K线顺序(最近10根):`);
    for (let i = 0; i < Math.min(10, validKlines.length); i++) {
        const k = validKlines[i];
        const openPrice = parseFloat(k.open_price);
        const closePrice = parseFloat(k.close_price);
        const change = closePrice - openPrice;
        const percentChange = (change / openPrice * 100).toFixed(2);
        const trend = change > 0 ? '上涨' : change < 0 ? '下跌' : '平';
        logWithCstTime(`  K线 #${i+1}: ${k.open_time.toISOString()}, 开:${openPrice}, 收:${closePrice}, 趋势:${trend}, 变化:${percentChange}%`);
    }
    
    // 如果有效K线不足，无法形成连续趋势
    if (validKlines.length < minRequiredCount) {
        logWithCstTime(`有效K线数量(${validKlines.length})少于所需最小连续数量(${minRequiredCount})，跳过检查`);
        return null;
    }
    
    // 预期的最近一根K线的时间（当前时间向下对齐到周期起始时间，再减去一个周期）
    const expectedLatestTime = new Date(Math.floor(now.getTime() / intervalMs) * intervalMs - intervalMs);
    logWithCstTime(`预期的最近K线时间: ${expectedLatestTime.toISOString()}`);
    
    // 检查是否是强制验证模式
    if (!forceCheck) {
        // 非强制模式下，验证最近K线时间是否与预期相符
        const actualLatestTime = validKlines[0].open_time;
        const timeDiff = Math.abs(actualLatestTime.getTime() - expectedLatestTime.getTime());
        
        logWithCstTime(`最近的K线实际时间: ${actualLatestTime.toISOString()}`);
        logWithCstTime(`与预期时间差异: ${timeDiff}ms, 相当于${(timeDiff / intervalMs).toFixed(2)}个周期`);
        
        // 如果时间差异太大，可能是数据不完整或时间不同步
        if (timeDiff > 2 * intervalMs) {
            logWithCstTime(`时间差异(${timeDiff}ms)超过2个周期，跳过检查。这可能是由于系统时间与K线数据不同步`);
            return null;
        }
    } else {
        logWithCstTime(`强制检查模式，跳过时间验证`);
    }
    
    // 寻找连续趋势K线
    let trendKlines = [];
    let consecutiveUp = 0;
    let consecutiveDown = 0;
    
    // 遍历有效K线
    for (let i = 0; i < validKlines.length; i++) {
        const kline = validKlines[i];
        
        // 计算K线涨跌
        const openPrice = parseFloat(kline.open_price);
        const closePrice = parseFloat(kline.close_price);
        const change = closePrice - openPrice;
        const isUp = change > 0;
        const isDown = change < 0;
        const percentChange = (change / openPrice * 100).toFixed(2);
        
        logWithCstTime(`分析K线 ${kline.open_time.toISOString()}, 开:${openPrice}, 收:${closePrice}, 变化:${percentChange}%`);
        
        // 如果是第一根K线，初始化趋势
        if (trendKlines.length === 0) {
            if (isUp) {
                consecutiveUp = 1;
                consecutiveDown = 0;
                trendKlines.push(kline);
                logWithCstTime(`  开始连续上涨，K线时间: ${kline.open_time.toISOString()}`);
            } else if (isDown) {
                consecutiveUp = 0;
                consecutiveDown = 1;
                trendKlines.push(kline);
                logWithCstTime(`  开始连续下跌，K线时间: ${kline.open_time.toISOString()}`);
            } else {
                // 平盘，跳过
                logWithCstTime(`  跳过平盘K线: ${kline.open_time.toISOString()}`);
                continue;
            }
        } else {
            // 判断是否与当前趋势一致
            if (isUp && consecutiveUp > 0) {
                consecutiveUp++;
                trendKlines.push(kline);
                logWithCstTime(`  继续上涨趋势，当前连续上涨: ${consecutiveUp}根`);
            } else if (isDown && consecutiveDown > 0) {
                consecutiveDown++;
                trendKlines.push(kline);
                logWithCstTime(`  继续下跌趋势，当前连续下跌: ${consecutiveDown}根`);
            } else {
                // 趋势不一致，结束检查
                logWithCstTime(`  趋势不一致(${isUp ? '上涨' : isDown ? '下跌' : '平'})，中断当前连续趋势检查`);
                break;
            }
        }
    }
    
    // 不改变原始trendKlines的排序，formatMessage函数将负责排序
    logWithCstTime(`找到的连续K线（按发现顺序）: ${trendKlines.length}根`);
    trendKlines.forEach((k, i) => {
        logWithCstTime(`  K线 #${i+1}: ${k.open_time.toISOString()}`);
    });
    
    // 检查是否达到了最小连续数量，并且是最近的K线
    if ((consecutiveUp >= minRequiredCount || consecutiveDown >= minRequiredCount) && 
        trendKlines.length >= minRequiredCount && 
        currentKline) {
        
        // 按时间排序，用于检查
        const sortedKlines = [...trendKlines].sort((a, b) => a.open_time.getTime() - b.open_time.getTime());
        const firstKlineTime = sortedKlines[0].open_time.getTime();
        const lastKlineTime = sortedKlines[sortedKlines.length - 1].open_time.getTime();
        
        // 最后的安全检查：确保找到的连续K线确实是最近的K线
        const expectedTime = expectedLatestTime.getTime();
        
        // 如果最近的K线与期望时间相差超过3个周期，这可能是不正确的数据
        const timeDiff = Math.abs(lastKlineTime - expectedTime);
        logWithCstTime(`最后安全检查 - 最新K线时间: ${new Date(lastKlineTime).toISOString()}, 期望时间: ${expectedLatestTime.toISOString()}`);
        logWithCstTime(`时间差异: ${timeDiff}ms, 相当于${(timeDiff / intervalMs).toFixed(2)}个周期`);
        
        if (timeDiff > 3 * intervalMs && !forceCheck) {
            logWithCstTime(`警告：找到的连续K线不是最近的K线。最新K线时间: ${new Date(lastKlineTime).toISOString()}, 期望时间: ${expectedLatestTime.toISOString()}`);
            logWithCstTime(`时间差异超过阈值(${3 * intervalMs}ms)，不触发提醒`);
            return null;
        } else if (forceCheck) {
            logWithCstTime(`强制检查模式，忽略时间差异检查`);
        } else {
            logWithCstTime(`时间差异在允许范围内，继续处理`);
        }
        
        // 检查当前K线的趋势
        const currentOpenPrice = parseFloat(currentKline.open_price);
        const currentPriceChange = currentPrice - currentOpenPrice;
        const currentDirection = currentPriceChange > 0 ? 'up' : currentPriceChange < 0 ? 'down' : null;
        
        // 记录当前K线趋势
        logWithCstTime(`当前K线开盘价: ${currentOpenPrice}, 当前价格: ${currentPrice}, 差值: ${currentPriceChange}`);
        logWithCstTime(`当前K线趋势: ${currentDirection || '平'}`);
        
        // 确定是向上还是向下的趋势
        const trend = consecutiveUp > 0 ? 'up' : 'down';
        logWithCstTime(`检测到的趋势: ${trend}, 连续${trend === 'up' ? '上涨' : '下跌'}K线数量: ${trend === 'up' ? consecutiveUp : consecutiveDown}`);
        
        // 格式化通知消息
        const message = formatMessage(symbol, interval, currentDirection, trendKlines, currentKline, currentPrice, now, forceCheck);
        logWithCstTime(`已生成通知消息，长度: ${message.length}字符`);
        
        return {
            type: trend,
            count: trendKlines.length,
            predictedCount: trendKlines.length + 1,
            currentPrice: currentPrice,
            currentChangePercent: ((currentPrice - currentOpenPrice) / currentOpenPrice * 100).toFixed(2),
            message: message,
            isCompleted: false,
            trendKlines: trendKlines
        };
    } else {
        if (trendKlines.length < minRequiredCount) {
            logWithCstTime(`连续K线数量(${trendKlines.length})不满足最小要求(${minRequiredCount})，不触发提醒`);
        } else if (!currentKline) {
            logWithCstTime(`未找到当前正在形成的K线，不触发提醒`);
        } else {
            logWithCstTime(`其他条件不满足，不触发提醒：上涨=${consecutiveUp}, 下跌=${consecutiveDown}, 需要=${minRequiredCount}, 当前K线=${currentKline ? '有' : '无'}`);
        }
    }
    
    return null;
}

// 检查提前预警趋势（比普通机器人少1根K线）
function checkEarlyWarningTrend(symbol, interval, completedKlines, currentKline, currentPrice, now, forceCheck = false) {
    try {
        // 配置是否可用
        if (!config) {
            config = loadConfig();
        }
        
        // 确定提前预警的连续K线阈值（固定为3）
        const minRequiredCount = 3; // 固定为3根，不再使用配置的动态值
        
        // 按时间排序K线，最近的K线排在前面
        const sortedKlines = [...completedKlines].sort((a, b) => b.open_time.getTime() - a.open_time.getTime());
        
        // 验证数据的时间连续性，只使用最近时间段内的K线
        let intervalMs;
        if (interval === '10m') {
            intervalMs = 10 * 60 * 1000;
        } else if (interval === '30m') {
            intervalMs = 30 * 60 * 1000;
        } else if (interval === '1h') {
            intervalMs = 60 * 60 * 1000;
        } else if (interval === '1d') {
            intervalMs = 24 * 60 * 60 * 1000;
        } else {
            intervalMs = 60 * 60 * 1000; // 默认1小时
        }
        
        // 最近时间段
        const cutoffTime = new Date(now.getTime() - 12 * intervalMs);
        const validKlines = sortedKlines.filter(kline => kline.open_time >= cutoffTime);
        
        // 记录排序后的K线时间顺序
        logWithCstTime(`提前预警检查 - K线顺序 (最新5根):`);
        for (let i = 0; i < Math.min(5, validKlines.length); i++) {
            const k = validKlines[i];
            const openPrice = parseFloat(k.open_price);
            const closePrice = parseFloat(k.close_price);
            const change = closePrice - openPrice;
            const trend = change > 0 ? '上涨' : change < 0 ? '下跌' : '平';
            logWithCstTime(`  K线 #${i+1}: ${k.open_time.toISOString()}, 开:${k.open_price}, 收:${k.close_price}, 涨跌:${trend}, 变化:${change.toFixed(2)}`);
        }
        
        // 计算我们期望的最近K线的开始时间
        const expectedLatestTime = new Date(Math.floor(now.getTime() / intervalMs) * intervalMs - intervalMs);
        logWithCstTime(`提前预警 - 当前时间: ${now.toISOString()}, 期望最近K线时间: ${expectedLatestTime.toISOString()}`);
        
        // 初始化变量
        let consecutiveUp = 0;
        let consecutiveDown = 0;
        let trendKlines = [];
        
        // 检查已完成K线的趋势
        for (let i = 0; i < validKlines.length; i++) {
            const kline = validKlines[i];
            const openPrice = parseFloat(kline.open_price);
            const closePrice = parseFloat(kline.close_price);
            const change = closePrice - openPrice;
            const trend = change > 0 ? '上涨' : change < 0 ? '下跌' : '平';
            
            // 记录检查的K线信息
            logWithCstTime(`提前预警检查K线 #${i+1}: ${kline.open_time.toISOString()}, 开:${openPrice}, 收:${closePrice}, 涨跌:${trend}, 变化:${change.toFixed(2)}`);
            
            // 判断当前K线是涨还是跌
            const isUp = change > 0;
            const isDown = change < 0;
            
            // 如果是第一根K线，初始化趋势
            if (trendKlines.length === 0) {
                if (isUp) {
                    consecutiveUp = 1;
                    consecutiveDown = 0;
                    trendKlines.push(kline);
                    logWithCstTime(`  开始连续上涨，K线时间: ${kline.open_time.toISOString()}`);
                } else if (isDown) {
                    consecutiveUp = 0;
                    consecutiveDown = 1;
                    trendKlines.push(kline);
                    logWithCstTime(`  开始连续下跌，K线时间: ${kline.open_time.toISOString()}`);
                } else {
                    // 平盘，跳过
                    logWithCstTime(`  跳过平盘K线: ${kline.open_time.toISOString()}`);
                    continue;
                }
            } else {
                // 判断是否与当前趋势一致
                if (isUp && consecutiveUp > 0) {
                    consecutiveUp++;
                    trendKlines.push(kline);
                    logWithCstTime(`  继续上涨趋势，当前连续上涨: ${consecutiveUp}根`);
                } else if (isDown && consecutiveDown > 0) {
                    consecutiveDown++;
                    trendKlines.push(kline);
                    logWithCstTime(`  继续下跌趋势，当前连续下跌: ${consecutiveDown}根`);
                } else {
                    // 趋势不一致，结束检查
                    logWithCstTime(`  趋势不一致，中断检查`);
                    break;
                }
            }
            
            // 一旦超过3根，就不再是我们想要提前预警的情况
            if (consecutiveUp > minRequiredCount || consecutiveDown > minRequiredCount) {
                logWithCstTime(`连续K线数(${consecutiveUp > 0 ? consecutiveUp : consecutiveDown})超过3根，不触发提前预警`);
                return null;
            }
        }
        
        // 只有在恰好是3根连续K线时才触发提前预警
        if ((consecutiveUp === minRequiredCount || consecutiveDown === minRequiredCount) &&
            trendKlines.length === minRequiredCount) {
            
            // 按时间排序，从早到晚
            const sortedTrendKlines = [...trendKlines].sort((a, b) => a.open_time.getTime() - b.open_time.getTime());
            
            logWithCstTime(`找到恰好${minRequiredCount}根连续${consecutiveUp > 0 ? "上涨" : "下跌"}K线，触发提前预警`);
            logWithCstTime(`提前预警K线详情（按时间排序）:`);
            
            for (let i = 0; i < sortedTrendKlines.length; i++) {
                const k = sortedTrendKlines[i];
                const openPrice = parseFloat(k.open_price);
                const closePrice = parseFloat(k.close_price);
                const change = closePrice - openPrice;
                const trend = change > 0 ? '上涨' : change < 0 ? '下跌' : '平';
                
                logWithCstTime(`  K线 #${i+1}: ${k.open_time.toISOString()}, 开:${k.open_price}, 收:${k.close_price}, 涨跌:${trend}, 变化:${change.toFixed(2)}`);
            }
            
            // 检查K线时间的合理性
            const firstKlineTime = sortedTrendKlines[0].open_time;
            const lastKlineTime = sortedTrendKlines[sortedTrendKlines.length - 1].open_time;
            const lastKlineEndTime = new Date(lastKlineTime.getTime() + intervalMs);
            
            // 最后的安全检查：确保找到的连续K线中的最后一根是最近的K线
            const expectedTime = expectedLatestTime.getTime();
            const timeDiff = Math.abs(lastKlineTime.getTime() - expectedTime);
            
            if (timeDiff > intervalMs) {
                logWithCstTime(`警告：找到的连续K线不是最近的K线。最新K线时间: ${lastKlineTime.toISOString()}, 期望时间: ${expectedLatestTime.toISOString()}`);
                logWithCstTime(`提前预警安全检查未通过，不触发提醒`);
                return null;
            }
            
            // 生成消息
            const trend = consecutiveUp > 0 ? 'up' : 'down';
            
            const message = formatMessage(
                symbol, 
                interval, 
                trend, 
                trendKlines, 
                currentKline, 
                currentPrice, 
                now,
                forceCheck,
                true // 添加isEarlyWarning参数为true
            );
            
            // 添加提前预警标记
            const earlyWarningMessage = message ;
            
            return {
                symbol,
                interval,
                type: trend,
                count: minRequiredCount,
                trendKlines,
                message: earlyWarningMessage,
                firstKlineTime,
                lastKlineTime: lastKlineEndTime,
                isEarlyWarning: true
            };
        }
        
        return null;
    } catch (error) {
        logWithCstTime(`检查提前预警趋势出错: ${error.message}`);
        return null;
    }
}

// 格式化消息
function formatMessage(symbol, interval, direction, trendKlines, currentKline, currentPrice, now, isTest = false, isEarlyWarning = false) {
    const arrow = direction === 'up' ? '📈' : '📉';
    const symbol_short = symbol.replace('USDT', '');
    
    // 确保trendKlines按时间排序（从早到晚）
    const sortedKlines = [...trendKlines].sort((a, b) => a.open_time.getTime() - b.open_time.getTime());
    
    // 最近的K线
    const lastKline = sortedKlines[sortedKlines.length - 1];
    
    // 记录K线时间顺序，帮助调试
    logWithCstTime(`消息格式化 - K线顺序：`);
    sortedKlines.forEach((k, i) => {
        logWithCstTime(`  K线 #${i+1}: ${k.open_time.toISOString()}`);
    });
    
    // 确定当前是否已经是东八区时间，通过检查系统环境变量或配置
    const isAlreadyCst = process.env.TZ === 'Asia/Shanghai' || process.env.NODE_ENV === 'production';
    logWithCstTime(`当前时区: ${isAlreadyCst ? 'CST(+8)' : 'UTC'}, 系统时区: ${process.env.TZ || '未设置'}`);
    
    // 格式化当前时间
    const currentDate = now.getDate().toString().padStart(2, '0');
    const currentMonth = (now.getMonth() + 1).toString().padStart(2, '0');
    const currentHours = now.getHours().toString().padStart(2, '0');
    const currentMinutes = now.getMinutes().toString().padStart(2, '0');
    const currentTimeStr = `${currentHours}:${currentMinutes} ${currentMonth}/${currentDate}`;

    // 计算当前K线开盘时间（基于当前时间和K线周期）
    let klineOpenPrice = 0;
    let klineOpenTimeStr = '';
    let priceChange = 0;
    let changeIcon = '';
    
    // 计算当前时间对应的K线开盘时间
    let klineOpenTime = new Date(now); // 从当前时间开始计算
    klineOpenTime.setSeconds(0, 0); // 重置秒和毫秒
    
    // 根据周期计算真实的开盘时间
    if (interval === '10m') {
        const minutes = klineOpenTime.getMinutes();
        // 向下取整到最近的10分钟周期 (0, 10, 20, 30, 40, 50)
        const roundedMinutes = Math.floor(minutes / 10) * 10;
        klineOpenTime.setMinutes(roundedMinutes);
    } else if (interval === '30m') {
        const minutes = klineOpenTime.getMinutes();
        // 向下取整到最近的30分钟周期 (0, 30)
        const roundedMinutes = Math.floor(minutes / 30) * 30;
        klineOpenTime.setMinutes(roundedMinutes);
    } else if (interval === '1h') {
        // 当前小时的开始
        klineOpenTime.setMinutes(0);
    } else if (interval === '1d') {
        // 当天的开始
        klineOpenTime.setHours(0, 0, 0, 0);
    }
    
    // 只有在不是东八区时间的情况下才加8小时
    let klineOpenTimeLocal;
    if (!isAlreadyCst) {
        // 加上8小时转为东八区时间（仅用于显示）
        klineOpenTimeLocal = new Date(klineOpenTime.getTime() + 8 * 60 * 60 * 1000);
        logWithCstTime(`UTC时间转CST: ${klineOpenTime.toISOString()} -> ${klineOpenTimeLocal.toISOString()}`);
    } else {
        // 如果已经是东八区时间，不需要再加8小时
        klineOpenTimeLocal = new Date(klineOpenTime);
        logWithCstTime(`已经是CST时间，保持不变: ${klineOpenTimeLocal.toISOString()}`);
    }
    
    // 检查特殊情况：特殊机器人在接近凌晨时，处理日期
    if (isEarlyWarning) {
        const nowDate = now.getDate();
        const nowHour = now.getHours();
        const nowMinute = now.getMinutes();
        
        // 如果是特殊机器人，且当前时间接近午夜（23点或0点），且K线时间的日期与当前日期不同
        if ((nowHour >= 23 || nowHour === 0) && klineOpenTimeLocal.getDate() !== nowDate) {
            logWithCstTime(`特殊机器人 - 检测到日期不匹配，修正K线时间从 ${klineOpenTimeLocal.toISOString()} 到当前日期`);
            // 将日期设置为当前日期，保持时分不变
            klineOpenTimeLocal.setDate(nowDate);
            klineOpenTimeLocal.setMonth(now.getMonth());
            klineOpenTimeLocal.setFullYear(now.getFullYear());
        }
    } else {
        // 检查是否跨越到第二天，如果是，则回退到当前日期
        const nowDate = now.getDate();
        // 如果k线时间的日期大于当前日期，说明是未来的日期，需要修正
        if (klineOpenTimeLocal.getDate() > nowDate && 
            klineOpenTimeLocal.getMonth() === now.getMonth() && 
            klineOpenTimeLocal.getFullYear() === now.getFullYear()) {
            logWithCstTime(`检测到未来日期，修正K线时间从 ${klineOpenTimeLocal.toISOString()} 到当前日期`);
            // 将日期设置为当前日期，保持时分不变
            klineOpenTimeLocal.setDate(nowDate);
        }
    }
    
    const klineHours = klineOpenTimeLocal.getHours().toString().padStart(2, '0');
    const klineMinutes = klineOpenTimeLocal.getMinutes().toString().padStart(2, '0');
    const klineDate = klineOpenTimeLocal.getDate().toString().padStart(2, '0');
    const klineMonth = (klineOpenTimeLocal.getMonth() + 1).toString().padStart(2, '0');
    klineOpenTimeStr = `${klineHours}:${klineMinutes} ${klineMonth}/${klineDate}`;
    
    // 记录计算的开盘时间，用于调试
    logWithCstTime(`计算的K线开盘时间 - ${interval} 周期: ${klineOpenTime.toISOString()}, 显示为: ${klineOpenTimeStr}, 是否特殊机器人: ${isEarlyWarning}`);
    
    // 使用currentKline计算价格变化（如果可用），否则使用最后一根完成K线的收盘价作为当前K线的开盘价
    if (currentKline && currentKline.open_price) {
        // 使用当前K线的开盘价
        klineOpenPrice = parseFloat(currentKline.open_price);
        logWithCstTime(`使用当前K线开盘价: ${klineOpenPrice}`);
    } else if (lastKline && lastKline.close_price) {
        // 如果没有当前K线，使用最后一根完成K线的收盘价作为当前K线的开盘价
        klineOpenPrice = parseFloat(lastKline.close_price);
        logWithCstTime(`使用最后一根K线收盘价作为开盘价: ${klineOpenPrice} (无当前K线)`);
    } else {
        // 如果都没有，使用当前价格
        klineOpenPrice = currentPrice;
        logWithCstTime(`无法获取K线数据，使用当前价格作为开盘价: ${klineOpenPrice}`);
    }
    
    // 计算价格变化（当前价格 - 开盘价格）
    priceChange = (currentPrice - klineOpenPrice).toFixed(2);
    
    // 根据趋势方向和价格变化选择图标
    // 1. 如果是上涨趋势: 正变化用🟢，负变化用🔴
    // 2. 如果是下跌趋势: 正变化用🔴，负变化用🟢
    if (direction === 'up') {
        changeIcon = parseFloat(priceChange) >= 0 ? '🟢' : '🔴';
    } else {
        changeIcon = parseFloat(priceChange) >= 0 ? '🔴' : '🟢';
    }
    
    // 构建新的消息模板，统一格式
    let message = `${symbol_short} - ${interval} - ${trendKlines.length} ${arrow}
当前价格：💲${currentPrice}u（${currentTimeStr}）
k线开盘价：💲${klineOpenPrice}u（${klineOpenTimeStr}）
涨幅：${changeIcon}${Math.abs(priceChange)}u`;

    // 如果是测试模式，添加测试标识
    if (isTest) {
        message += `\n⚠️测试模式-非实际信号⚠️`;
    }

    return message;
}

// 发送企业微信通知
async function sendWeComNotification(message, isEarlyWarning = false) {
    try {
        // 构建请求体
        const payload = {
            msgtype: 'text',
            text: {
                content: message
            }
        };
        
        // 选择要发送到的Webhook
        const webhooks = isEarlyWarning ? EARLY_WARNING_WEBHOOKS : WECOM_WEBHOOKS;
        
        // 记录将要发送的信息
        logWithCstTime(`准备发送${isEarlyWarning ? '提前预警' : '标准'}通知到 ${webhooks.length} 个Webhook`);
        logWithCstTime(`消息内容(${message.length}字符):\n${message}`);
        
        // 记录每个webhook的URL
        webhooks.forEach((webhook, index) => {
            logWithCstTime(`Webhook #${index + 1}: ${webhook}`);
        });

        // 记录系统时间信息
        const now = new Date();
        logWithCstTime(`发送通知时系统时间: ${now.toISOString()} (UTC), 时间戳: ${now.getTime()}`);
        logWithCstTime(`发送通知时本地时间: ${now.toString()}`);
        
        // 发送到所有配置的Webhook
        const results = await Promise.all(webhooks.map(async (webhook, index) => {
            try {
                logWithCstTime(`开始发送到Webhook #${index + 1}: ${webhook}`);
                logWithCstTime(`请求体: ${JSON.stringify(payload)}`);
                
                const startTime = Date.now();
                const response = await axios.post(webhook, payload, {
                    headers: {
                        'Content-Type': 'application/json'
                    },
                    timeout: 10000 // 设置10秒超时
                });
                const endTime = Date.now();
                const duration = endTime - startTime;
                
                const status = response.data && response.data.errcode === 0 ? '成功' : '失败';
                logWithCstTime(`通知发送到 ${webhook} ${status}, 响应时间: ${duration}ms, 响应状态: ${response.status}`);
                logWithCstTime(`完整响应数据: ${JSON.stringify(response.data)}`);
                
                return response.data && response.data.errcode === 0;
            } catch (error) {
                logWithCstTime(`发送到 ${webhook} 出错: ${error.message}`);
                logWithCstTime(`错误类型: ${error.name}, 错误代码: ${error.code || '无'}`);
                
                if (error.response) {
                    logWithCstTime(`错误HTTP状态: ${error.response.status}`);
                    logWithCstTime(`错误响应头: ${JSON.stringify(error.response.headers)}`);
                    logWithCstTime(`错误响应数据: ${JSON.stringify(error.response.data)}`);
                }
                
                if (error.request) {
                    logWithCstTime(`请求已发送但未收到响应，请求详情: ${error.request._currentUrl || '未知URL'}`);
                }
                
                // 记录完整错误堆栈，帮助调试
                logWithCstTime(`错误堆栈: ${error.stack}`);
                
                return false;
            }
        }));
        
        // 汇总结果
        const successCount = results.filter(result => result === true).length;
        logWithCstTime(`通知发送完成：成功 ${successCount}/${webhooks.length} 个Webhook`);
        
        // 如果所有webhook都发送成功，返回true
        return results.every(result => result === true);
    } catch (error) {
        logWithCstTime(`发送企业微信通知出错: ${error.stack || error.message}`);
        logWithCstTime(`错误类型: ${error.name}, 错误代码: ${error.code || '无'}`);
        return false;
    }
}

// 检查通知是否已发送
function isAlertSent(alertKey) {
    const lastAlertTime = lastAlerts.get(alertKey);
    return !!lastAlertTime;
}

// 保存已发送的通知
function saveAlertSent(alertKey) {
    lastAlerts.set(alertKey, Date.now());
}

// 获取简化的交易对名称
function getShortSymbol(symbol) {
    // 移除后缀如USDT, BTC等
    const symbolShort = symbol.replace(/USDT$|BTC$|ETH$|USD$/, '');
    return symbolShort;
}

// 简化后的monitor函数
async function monitor(symbol, interval, forceCheck = false) {
    try {
        // 获取当前时间
        const now = new Date();
        const minute = now.getMinutes();
        
        // 记录该函数被调用的详细信息
        logWithCstTime(`===== 监控函数调用 - ${symbol} ${interval} =====`);
        logWithCstTime(`系统时间: ${now.toISOString()}, 时间戳: ${now.getTime()}, 分钟: ${minute}`);
        logWithCstTime(`强制检查模式: ${forceCheck ? '是' : '否'}`);
        
        // 检查当前时间是否应该执行该周期的检查
        // 仅在非强制检查模式下检查
        if (!forceCheck) {
            logWithCstTime(`检查当前分钟(${minute})是否是${interval}的检查点...`);
            const shouldCheckNow = shouldCheck(minute, interval);
            if (!shouldCheckNow) {
                logWithCstTime(`当前分钟不是${interval}的检查点，跳过执行`);
                return;
            }
            logWithCstTime(`当前分钟是${interval}的检查点，继续执行检查`);
        } else {
            logWithCstTime(`强制检查模式，跳过时间检查点验证`);
        }
        
        // 执行监控检查
        logWithCstTime(`开始执行${symbol} ${interval}的连续K线检查...`);
        const result = await checkConsecutiveKlinesAndPredict(symbol, interval, forceCheck);
        
        // 记录检查结果
        if (!result) {
            logWithCstTime(`${symbol} ${interval}检查完成，未找到符合条件的连续K线`);
            return;
        }
        
        // 如果检查有结果，则发送通知
        logWithCstTime(`发现 ${symbol} ${interval} 的连续${result.type === 'up' ? '上涨' : '下跌'}K线: ${result.count}根`);
        
        // 记录每根K线的时间和价格变化
        const klinesInfo = result.trendKlines.map((k, idx) => {
            const openPrice = parseFloat(k.open_price);
            const closePrice = parseFloat(k.close_price);
            const change = closePrice - openPrice;
            const percentChange = (change / openPrice * 100).toFixed(2);
            const time = k.open_time.toISOString();
            return `K线#${idx+1}: ${time}, 开:${openPrice}, 收:${closePrice}, 变化:${change.toFixed(2)}(${percentChange}%)`;
        }).join('\n');
        
        logWithCstTime(`连续K线详情:\n${klinesInfo}`);
        
        // 准备发送通知所需的参数
        // 确保K线集合已排序
        const sortedKlines = [...result.trendKlines].sort((a, b) => a.open_time.getTime() - b.open_time.getTime());
        const firstKlineTime = sortedKlines[0].open_time;
        const lastKlineTime = sortedKlines[sortedKlines.length - 1].close_time;
        
        // 当强制检查时，添加时间戳以避免重复通知过滤
        const forceTimestamp = forceCheck ? Date.now() : null;
        if (forceTimestamp) {
            logWithCstTime(`强制检查模式，使用时间戳${forceTimestamp}防止通知去重`);
        }
        
        // 发送通知
        logWithCstTime(`准备发送${symbol} ${interval}连续${result.type}趋势的通知...`);
        await sendNotification(
            symbol,
            interval,
            result.type,
            result.message,
            result.count,
            firstKlineTime,
            lastKlineTime,
            result.isEarlyWarning || false,
            forceTimestamp
        );
        
        logWithCstTime(`${symbol} ${interval}监控检查和通知流程完成`);
        logWithCstTime(`===== 监控函数结束 - ${symbol} ${interval} =====`);
    } catch (error) {
        logWithCstTime(`监控${symbol} ${interval}时出错: ${error.message}`);
        logWithCstTime(`错误堆栈: ${error.stack}`);
    }
}

// 发送通知
async function sendNotification(symbol, interval, type, messageText, count, firstKlineTime, lastKlineTime, isEarlyWarning = false, forceTimestamp = null) {
    try {
        // 生成通知的唯一键
        const symbolShort = getShortSymbol(symbol);
        logWithCstTime(`处理通知 - 交易对: ${symbol}, 简写: ${symbolShort}, 周期: ${interval}, 类型: ${type}, K线数量: ${count}`);
        logWithCstTime(`K线时间范围: 第一根=${firstKlineTime.toISOString()}, 最后一根=${lastKlineTime.toISOString()}`);
        logWithCstTime(`是特殊预警: ${isEarlyWarning ? '是' : '否'}, 强制时间戳: ${forceTimestamp || '无'}`);
        
        // 如果是特殊机器人，且不是BTC交易对，则跳过
        if (isEarlyWarning && !symbol.startsWith('BTC')) {
            logWithCstTime(`特殊机器人不推送非BTC交易对: ${symbol}`);
            return;
        }
        
        // 如果提供了强制时间戳，则将其添加到通知键中以确保唯一性
        let alertKey;
        if (forceTimestamp) {
            // 添加时间戳确保每次强制检查都会发送通知
            alertKey = `${symbolShort}_${interval}_${type}_${count}_${lastKlineTime.getTime()}_force_${forceTimestamp}`;
            logWithCstTime(`强制检查模式，使用唯一通知键: ${alertKey}`);
        } else {
            alertKey = `${symbolShort}_${interval}_${type}_${count}_${lastKlineTime.getTime()}`;
            logWithCstTime(`常规模式，使用通知键: ${alertKey}`);
        }
        
        // 如果是提前预警通知，使用不同的键名称
        const notificationKey = isEarlyWarning ? `earlywarning_${alertKey}` : alertKey;
        logWithCstTime(`最终通知键: ${notificationKey}`);
        
        // 检查是否已经发送过该通知
        let skipNotification = false;
        
        // 当在强制检查模式下，直接跳过通知去重检查
        if (forceTimestamp) {
            logWithCstTime(`强制检查模式，跳过通知去重检查，确保始终发送通知`);
        } else {
            // 只在非强制模式下检查是否已经发送过此通知
            const alertSent = isAlertSent(notificationKey);
            logWithCstTime(`检查是否已发送过通知: ${notificationKey}, 结果: ${alertSent ? '已发送' : '未发送'}`);
            
            if (alertSent) {
                logWithCstTime(`已发送过该通知，跳过: ${notificationKey}`);
                skipNotification = true;
            } else {
                // 保存通知状态（只在非强制模式下保存，避免干扰正常检查）
                saveAlertSent(notificationKey);
                logWithCstTime(`记录通知状态: ${notificationKey}, 当前缓存大小: ${lastAlerts.size}`);
                
                // 记录lastAlerts缓存的部分内容，以便调试
                let cacheEntries = [];
                let counter = 0;
                for (const [key, value] of lastAlerts.entries()) {
                    if (counter++ < 5) { // 仅记录前5条
                        cacheEntries.push(`${key}: ${new Date(value).toISOString()}`);
                    } else {
                        break;
                    }
                }
                if (cacheEntries.length > 0) {
                    logWithCstTime(`通知缓存样本(${lastAlerts.size}项):\n${cacheEntries.join('\n')}`);
                }
            }
        }
        
        // 如果需要跳过通知，则直接返回
        if (skipNotification && !forceTimestamp) {
            logWithCstTime(`由于去重逻辑，跳过发送通知: ${notificationKey}`);
            return;
        }
        
        // 准备发送通知
        logWithCstTime(`准备发送通知: ${notificationKey}`);
        logWithCstTime(`通知消息内容(${messageText.length}字符):\n${messageText}`);
        
        // 记录系统时间，用于日志
        const now = new Date();
        logWithCstTime(`当前系统时间: ${now.toISOString()}, 时间戳: ${now.getTime()}, 本地时间: ${now.toString()}`);
        
        // 根据目标类型发送到相应的通知渠道
        if (isEarlyWarning) {
            // 提前预警通知只发送到特定Webhook
            logWithCstTime(`发送提前预警通知: ${notificationKey}`);
            const result = await sendWeComNotification(messageText, true);
            logWithCstTime(`已发送提前预警通知: ${notificationKey}, 结果: ${result ? '成功' : '失败'}`);
        } else {
            // 标准通知发送到所有配置的通知渠道
            logWithCstTime(`发送标准通知: ${notificationKey}`);
            const result = await sendWeComNotification(messageText, false);
            logWithCstTime(`已发送标准通知: ${notificationKey}, 结果: ${result ? '成功' : '失败'}`);
        }
    } catch (error) {
        logWithCstTime(`发送通知出错: ${error.stack || error.message}`);
        logWithCstTime(`错误类型: ${error.name}, 错误代码: ${error.code || '无'}`);
    }
}

// 启动监控
function startMonitor() {
    try {
        logWithCstTime('=== 启动监控服务 ===');
        
        // 显示配置信息
        logWithCstTime(`配置信息:`);
        const config = loadConfig();
        if (!config) {
            logWithCstTime('配置加载失败，无法启动监控服务');
            return;
        }
        logWithCstTime(`交易对: ${config.symbols.join(', ')}`);
        logWithCstTime(`时间间隔: ${config.intervals.join(', ')}`);
        logWithCstTime(`连续K线数量: ${config.consecutiveCount}`);
        
        // 显示检查点信息
        logWithCstTime(`检查间隔: 每分钟`);
        for (const interval in config.checkPoints) {
            logWithCstTime(`  ${interval}: 每小时的第 ${config.checkPoints[interval].join(', ')} 分钟`);
        }
        
        logWithCstTime('注意: 10分钟K线由5分钟K线合成');

        // 首次运行任务
        logWithCstTime('首次运行监控任务...');
        runMonitorTasks().then(() => {
            logWithCstTime('首次任务完成');
            
            // 设置定时任务，每分钟执行一次
            logWithCstTime('K线监控服务已启动...');
            schedule.scheduleJob('* * * * *', async () => {
                try {
                    await runMonitorTasks();
                } catch (error) {
                    logWithCstTime(`执行监控任务错误: ${error.message}`);
                }
            });
            
            logWithCstTime('=== 监控服务启动成功 ===');
        }).catch(error => {
            logWithCstTime(`首次执行监控任务失败: ${error.message}`);
        });
    } catch (error) {
        logWithCstTime(`启动监控服务失败: ${error.message}`);
    }
}

// 获取配置
function getConfig() {
    return loadConfig();
}

// 执行一次监控任务
async function runMonitorTasks() {
    try {
        logWithCstTime('===== 开始执行监控任务批次 =====');
        
        const config = loadConfig();
        if (!config) {
            logWithCstTime('配置加载失败，无法执行监控任务');
            return;
        }
        
        // 记录当前时间
        const now = new Date();
        logWithCstTime(`开始执行监控任务: ${now.toLocaleString()}`);
        
        // 遍历配置的交易对和时间间隔
        for (const symbol of config.symbols) {
            for (const interval of config.intervals) {
                try {
                    await monitor(symbol, interval);
                } catch (error) {
                    logWithCstTime(`监控任务错误 ${symbol} ${interval}: ${error.message}`);
                }
            }
        }
        
        logWithCstTime(`监控任务执行完成: ${new Date().toLocaleString()}`);
        logWithCstTime('===== 监控任务批次结束 =====');
    } catch (error) {
        logWithCstTime(`执行监控任务失败: ${error.message}`);
    }
}

// 监控任务入口
async function runMonitorTask() {
    try {
        logWithCstTime('====== 启动币安K线监控任务 ======');
        
        // 加载配置
        config = loadConfig();
        
        if (!config) {
            logWithCstTime('配置加载失败，停止监控');
            return;
        }
        
        // 检查是否已经清空过期的报警缓存
        const now = new Date();
        if (!lastCacheClearTime || (now.getTime() - lastCacheClearTime) > 24 * 60 * 60 * 1000) {
            logWithCstTime('清理过期警报缓存');
            lastAlerts.clear();
            lastCacheClearTime = now.getTime();
        }
        
        // 运行监控
        await monitor();
        
        logWithCstTime('====== 币安K线监控任务完成 ======');
    } catch (error) {
        logWithCstTime(`监控任务运行出错: ${error.message}`);
    }
}

// 格式化日期的函数
function formatDate(date) {
    // 确保date是Date对象
    if (!(date instanceof Date)) {
        date = new Date(date);
    }
    
    // 获取年月日时分
    const year = date.getFullYear();
    const month = String(date.getMonth() + 1).padStart(2, '0');
    const day = String(date.getDate()).padStart(2, '0');
    const hours = String(date.getHours()).padStart(2, '0');
    const minutes = String(date.getMinutes()).padStart(2, '0');
    
    // 返回格式化的时间字符串
    return `${year}/${month}/${day} ${hours}:${minutes}`;
}

// 延时函数
function sleep(ms) {
    return new Promise(resolve => setTimeout(resolve, ms));
}

// 确保正确导出所需函数
module.exports = {
    monitor,
    getConfig,
    startMonitor
};